package com.dynaBlaster.gameElements;

import com.dynaBlaster.BoardView;
import com.dynaBlaster.DynaView;
import com.dynaBlaster.GameConfg;
import com.dynaBlaster.Point;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;

public abstract class Creature {

	public enum Direction {
		NORTH, SOUTH, EAST, WEST;
	}

	public boolean IS_FLYING = false;
	public boolean IS_PASSING_CREATURES = true;
	protected Point pos;

	public static int CREATURE_ALIVE = 0;
	public static int CREATURE_DEAD = 1;
	private int state = CREATURE_ALIVE;

	Direction dir = Direction.EAST;

	public int speed;

	protected int size;

	Bitmap[] bitmapNSEW;

	protected DynaView board;

	public Creature(DynaView board) {
		this.board = board;
		speed = BoardView.mTileSize / GameConfg.CREATURE_SPEED_FACTOR;
		size = BoardView.mTileSize / GameConfg.CREATURE_SIZE_FACTOR;// z
		// obcieciem
	}

	/**
	 * Set 4 images for the creature each representing it looking in different
	 * direction
	 * 
	 * @param imgs
	 *            : [NORTH, SOUTH, EAST, WEST ]
	 */
	void loadImg(Drawable[] nsew) {
		int img_size = size * 2 + 1;

		bitmapNSEW = new Bitmap[4];
		for (int i = 0; i < 4; i++) {
			Drawable db = nsew[i];
			Bitmap bitmap = Bitmap.createBitmap(img_size, img_size, Bitmap.Config.ARGB_8888);
			Canvas canvasBmp = new Canvas(bitmap);
			db.setBounds(0, 0, img_size, img_size);
			db.draw(canvasBmp);

			bitmapNSEW[i] = bitmap;
		}
	}

	/**
	 * loads and returns a bitmap with drawble
	 */
	Bitmap loadImg(Drawable drawable) {

		int img_size = size * 2 + 1;
		Bitmap bitmap = Bitmap.createBitmap(img_size, img_size, Bitmap.Config.ARGB_8888);
		Canvas canvasBmp = new Canvas(bitmap);
		drawable.setBounds(0, 0, img_size, img_size);
		drawable.draw(canvasBmp);

		return bitmap;
	}

	public void draw(Canvas canvas) {
		canvas.drawBitmap(bitmapNSEW[dir.ordinal()], pos.x - size, pos.y - size, new Paint());
		/*
		 * Paint p = new Paint(); canvas.drawCircle(pos.x-size, pos.y-size, 2,
		 * new Paint()); canvas.drawCircle(pos.x, pos.y, 4, new Paint());
		 * 
		 * int startX=pos.x , startY=pos.y, stopX=startX, stopY=startY; switch
		 * (dir){ case NORTH: stopY-=speed*3; canvas.drawLine(startX, startY,
		 * stopX, stopY, new Paint()); canvas.drawCircle(stopX, stopY,3, new
		 * Paint()); break; case SOUTH: stopY+=speed*3; canvas.drawLine(startX,
		 * startY, stopX, stopY, new Paint()); canvas.drawCircle(stopX, stopY,3,
		 * new Paint()); break; case EAST: stopX+=speed*3;
		 * canvas.drawLine(startX, startY, stopX, stopY, new Paint());
		 * canvas.drawCircle(stopX, stopY,3, new Paint()); break; case WEST:
		 * stopX-=speed*3; canvas.drawLine(startX, startY, stopX, stopY, new
		 * Paint()); canvas.drawCircle(stopX, stopY,3, new Paint()); break; }
		 * canvas.drawCircle(pos.x, pos.y, 4, new Paint());
		 */
	}

	abstract public void move();

	Point nextAfterMove() {
		return checkMoveInDirection(dir);
	}

	Point checkMoveInDirection(Direction direction) {

		Point nextPos = new Point(this.pos);

		Point toCheck1 = null; // checking 2 corners facing move direction
		Point toCheck2 = null;

		switch (direction) {
		case NORTH:
			nextPos.y -= speed;

			toCheck1 = new Point(nextPos);
			toCheck1.x -= size;
			toCheck1.y -= size;

			toCheck2 = new Point(nextPos);
			toCheck2.x += size;
			toCheck2.y -= size;
			break;
		case SOUTH:
			nextPos.y += speed;

			toCheck1 = new Point(nextPos);
			toCheck1.x -= size;
			toCheck1.y += size;

			toCheck2 = new Point(nextPos);
			toCheck2.x += size;
			toCheck2.y += size;
			break;
		case EAST:
			nextPos.x += speed;

			toCheck1 = new Point(nextPos);
			toCheck1.x += size;
			toCheck1.y -= size;

			toCheck2 = new Point(nextPos);
			toCheck2.x += size;
			toCheck2.y += size;
			break;
		case WEST:
			nextPos.x -= speed;

			toCheck1 = new Point(nextPos);
			toCheck1.x -= size;
			toCheck1.y -= size;

			toCheck2 = new Point(nextPos);
			toCheck2.x -= size;
			toCheck2.y += size;
			break;
		}

		if (this.checkFree(toCheck1) && this.checkFree(toCheck2))
			return nextPos;
		else
			return null;

	}

	/**
	 * for cvreature computes all available directions. randomly chooses one of
	 * them and moves creature there. is no dir available stays in place and
	 * returns false
	 * 
	 * @return true if crature was moved false if it is blocked
	 */
	protected boolean moveInRandomFreeDirection() {
		int count = 0;
		Point next;
		Point[] tab = new Point[4];
		Direction[] tabDir = new Direction[4];

		this.setDir(Direction.NORTH);
		next = nextAfterMove();
		if (next != null) {
			tab[count] = next;
			tabDir[count] = Direction.NORTH;
			count++;
		}

		this.setDir(Direction.SOUTH);
		next = nextAfterMove();
		if (next != null) {
			tab[count] = next;
			tabDir[count] = Direction.SOUTH;
			count++;
		}

		this.setDir(Direction.EAST);
		next = nextAfterMove();
		if (next != null) {
			tab[count] = next;
			tabDir[count] = Direction.EAST;
			count++;
		}

		this.setDir(Direction.WEST);
		next = nextAfterMove();
		if (next != null) {
			tab[count] = next;
			tabDir[count] = Direction.WEST;
			count++;
		}

		if (count == 0)
			return false;
		if (count == 1) {
			this.setDir(tabDir[0]);
			this.pos = tab[0];
			return true;
		}

		int ind = DynaView.RNG.nextInt(count);

		this.setDir(tabDir[ind]);
		this.pos = tab[ind];
		return true;
	}

	protected boolean checkFree(Point toCheck) {
		return board.isFree(toCheck, IS_PASSING_CREATURES, IS_FLYING, null);
	}

	public void setDir(Direction dir) {
		this.dir = dir;
	}

	public Direction getDir() {
		return this.dir;
	}

	public Point getPos() {
		return pos;
	}

	public void setPos(Point pos) {
		this.pos = pos;
	}

	/**
	 * returns true when this creature posisition, and its size provide covering
	 * of a point creatures are rectangular, not circular
	 * 
	 * @param toCheck
	 * @return
	 */
	public boolean isOverPoint(Point toCheck) {
		int dX = toCheck.x - pos.x;
		int dY = toCheck.y - pos.y;

		dX = Math.abs(dX);
		dY = Math.abs(dY);

		if (dX <= size && dY <= size)
			return true;
		else
			return false;
	}

	/**
	 * returns true when this creature touches other creatures are rectangular,
	 * not circular
	 */
	public boolean touchesCreature(Creature other) {
		int dX = other.pos.x - pos.x;
		int dY = other.pos.y - pos.y;

		dX = Math.abs(dX);
		dY = Math.abs(dY);

		if (dX < this.size + other.size && dY < this.size + other.size)
			return true;
		else
			return false;
	}

	protected boolean isOnFieldMiddle() {
		return (pos.x - size - BoardView.mXOffset) % BoardView.mTileSize < GameConfg.CREATURE_isOnFieldMiddle
				&& (pos.y - size - BoardView.mYOffset) % BoardView.mTileSize < GameConfg.CREATURE_isOnFieldMiddle;
	}

	public int getState() {
		return state;
	}

	public void kill() {
		this.state = CREATURE_DEAD;
	}

	public boolean isAlive() {
		return state == CREATURE_ALIVE;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((dir == null) ? 0 : dir.hashCode());
		result = prime * result + ((pos == null) ? 0 : pos.hashCode());
		result = prime * result + speed;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Creature other = (Creature) obj;
		if (dir == null) {
			if (other.dir != null)
				return false;
		} else if (!dir.equals(other.dir))
			return false;
		if (pos == null) {
			if (other.pos != null)
				return false;
		} else if (!pos.equals(other.pos))
			return false;
		if (speed != other.speed)
			return false;
		return true;
	}

}
